import styled from 'styled-components';
import React, { CSSProperties, useState, useCallback, useEffect, useRef, ReactNode } from 'react';
import Dagre from '@dagrejs/dagre';
import {
    ReactFlowProvider,
    useNodesState,
    useEdgesState,
    useReactFlow,
    Panel,
    ReactFlow, Controls, Background, Node, Edge
} from 'reactflow';
import 'reactflow/dist/style.css'

export interface ObjectRelationGraphProps {
    style?: CSSProperties;
    nodes: Array<Node>;
    edges: Array<Edge>;
    version?: number;
    tools?: ReactNode;
    direction?: 'TB' | 'LR';
}

export const getLayoutedElements = (nodes, edges, options) => {
    const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
    g.setGraph({ rankdir: options.direction });

    edges.forEach((edge) => g.setEdge(edge.source, edge.target));
    nodes.forEach((node) =>
        g.setNode(node.id, {
            ...node,
            width: node.measured?.width ?? 0,
            height: node.measured?.height ?? 0,
        }),
    );

    Dagre.layout(g);

    return {
        nodes: nodes.map((node) => {
            const position = g.node(node.id);
            // We are shifting the dagre node position (anchor=center center) to the top left
            // so it matches the React Flow node anchor point (top left).
            const x = position.x - (node.measured?.width ?? 0) / 2;
            const y = position.y - (node.measured?.height ?? 0) / 2;

            return { ...node, position: { x, y } };
        }),
        edges,
    };
};

const LayoutFlow: React.FC<ObjectRelationGraphProps> = (props) => {

    const { fitView } = useReactFlow();

    const [nodes, setNodes, onNodesChange] = useNodesState(props.nodes);

    const [edges, setEdges, onEdgesChange] = useEdgesState(props.edges);

    const onLayout = (nodes, edges, direction) => {
        const layouted = getLayoutedElements(nodes, edges, {
            direction: direction
        });

        setNodes([...layouted.nodes]);
        setEdges([...layouted.edges]);

        window.requestAnimationFrame(() => {
            fitView();
        });
    };

    useEffect(() => {
        onLayout(props.nodes, props.edges, !props.direction ? 'TB' : props.direction);
    }, [props.nodes, props.edges]);

    useEffect(() => {
        onLayout(nodes, edges, !props.direction ? 'TB' : props.direction);
    }, [props.direction, props.version]);

    return (
        <Container style={props.style}>
            <ReactFlow
                nodes={nodes}
                edges={edges}
                onNodesChange={onNodesChange}
                onEdgesChange={onEdgesChange}
                fitView>
                <Background />
                <Controls style={{ marginBottom: '50px', marginLeft: '5px' }} />
                {
                    props.tools && <Panel position="bottom-left" style={{ width: '100%', height: '45px', margin: '0', backgroundColor: '#FFFFFF' }}>
                        {props.tools}
                    </Panel>
                }
            </ReactFlow>
        </Container>
    );
}

export const ObjectRelationGraph: React.FC<ObjectRelationGraphProps> = (props) => {


    return (
        <Container style={props.style}>
            <ReactFlowProvider>
                <LayoutFlow {...props} />
            </ReactFlowProvider>
        </Container>
    );
}


const Container = styled.div`
    width: 100%;
    height: 100%;
`;


